/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package ecodex.restapi;

import javax.ws.rs.client.*;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Form;
import java.util.ArrayList;
import java.util.UUID;
import org.glassfish.jersey.client.ClientProperties;
import javax.ws.rs.ProcessingException;
import ecodex.restapi.models.*;
import ecodex.serviciospruebas.Utils;
import ecodex.restapi.exceptions.*;

/**
 *
 * @author jjcarbajal
 */
public class RestApi {
    private final String BASE_ADRESS;
    private final String ACTION_TOKEN = "/Token";
    private final String GRAND_TYPE ="authorization_token";
    private final String ACTION_DOCUMENTOS = "/api/Documentos";
    private final String ACTION_CERTIFICADOS_CLAVE = "/api/Certificados/Clave"; 

    public RestApi() {
        BASE_ADRESS = Utils.getPropertyValue("BASE_ADRESS");
    }
    
    /**
     * Servicio REST para obtener el Token de seguridad
     * @param rfc
     * @param integrador
     * @return
     * @throws FallaAplicacion
     * @throws FallaValidacion
     * @throws FallaSesion
     * @throws Exception 
     */
    public Token getToken(String rfc, String integrador) 
            throws FallaAplicacion, FallaValidacion, FallaSesion, Exception {
        String json = "";
        try {

            Client client = ClientBuilder.newClient();
            WebTarget target = client.target(BASE_ADRESS).path(ACTION_TOKEN);
            Form form = new Form();
            form.param("rfc", rfc.trim());
            form.param("integrador", integrador.trim());
            form.param("grant_type", GRAND_TYPE);
            Response response = target
                                .request(MediaType.APPLICATION_FORM_URLENCODED)
                                .accept(MediaType.APPLICATION_JSON)
                                .post(Entity.form(form));
            
            json = response.readEntity(String.class);
            if (response.getStatus() != 200)
                throw new ProcessingException("");
            
            Token token = JsonConvert.<Token>deserializeObject(Token.class, json);

            return token;
            
        }catch (ProcessingException ex) {
            String mensaje = ex.getMessage();
            if (mensaje != null && !"".equals(mensaje))
                throw new FallaAplicacion(mensaje);
            
            throw ControlException(json);
        }catch (Exception ex) {
            throw new FallaAplicacion(ex.getMessage());
        }
    }
    
    /**
     * Obtiene una lista de los UUIDs mas recientes que han sido timbrados vía REST
     * @param token
     * @return
     * @throws FallaAplicacion
     * @throws FallaValidacion
     * @throws FallaSesion
     * @throws Exception 
     */
    public ArrayList<DocumentoInfo> getDocumentos(Token token) 
        throws FallaAplicacion, FallaValidacion, FallaSesion, Exception {
        
        String json = "";
        try {
            Client client = ClientBuilder.newClient();
            WebTarget target = client.target(BASE_ADRESS + ACTION_DOCUMENTOS);
            Response response = target
                                .request(MediaType.APPLICATION_FORM_URLENCODED)
                                .accept(MediaType.APPLICATION_JSON)
                                .header("Authorization", token.getToken_type() + " " + token.getAccess_token())
                                .get();
            
            json = response.readEntity(String.class);
            if (response.getStatus() != 200)
                throw new ProcessingException("");
            
            ArrayList<DocumentoInfo> docs = JsonConvert.<DocumentoInfo, ArrayList<DocumentoInfo>>deserializeObjectList(DocumentoInfo.class, json);
            return docs;
        }
        catch (ProcessingException ex) {
            String mensaje = ex.getMessage();
            if (mensaje != null && !"".equals(mensaje))
                throw new FallaAplicacion(mensaje);
            
            throw ControlException(json);
        }                
        catch (Exception ex) {
            throw new FallaAplicacion(ex.getMessage());
        }      
    }
    
    /**
     * Servicio REST para recuperar un documento por Hash
     * @param token
     * @param hash
     * @return
     * @throws FallaAplicacion
     * @throws FallaValidacion
     * @throws FallaSesion
     * @throws Exception 
     */
    public String getDocumento(Token token, String hash) 
            throws FallaAplicacion, FallaValidacion, FallaSesion, Exception {
        
        String xml = "";
        try {
            Client client = ClientBuilder.newClient();
            WebTarget target = client.target(BASE_ADRESS + ACTION_DOCUMENTOS).path(hash.trim());
            Response response = target
                                .request()
                                .header("Authorization", token.getToken_type() + " " + token.getAccess_token())
                                .get();
            
            xml = response.readEntity(String.class);
            if (response.getStatus() != 200)
                throw new ProcessingException("");
            
            return xml;
        }
        catch (ProcessingException ex) {
            String mensaje = ex.getMessage();
            if (mensaje != null && !"".equals(mensaje))
                throw new FallaAplicacion(mensaje);
            
            throw ControlException(xml);
        }                
        catch (Exception ex) {
            throw new FallaAplicacion(ex.getMessage());
        }
    }
    
    /**
     * Servicio REST para obtener el token y URL para la carga del Certificado del Cliente Emisor
     * @param token
     * @return
     * @throws FallaAplicacion
     * @throws FallaValidacion
     * @throws FallaSesion
     * @throws Exception 
     */
    public CertificadoClave getClaveCertificado(Token token) 
        throws FallaAplicacion, FallaValidacion, FallaSesion, Exception {
        
        String json = "";
        try {
            Client client = ClientBuilder.newClient();
            WebTarget target = client.target(BASE_ADRESS).path(ACTION_CERTIFICADOS_CLAVE);
            Response response = target
                                .request()
                                .header("Authorization", token.getToken_type() + " " + token.getAccess_token())
                                .get();
            
            json = response.readEntity(String.class);
            if (response.getStatus() != 200)
                throw new ProcessingException("");
            
            CertificadoClave cert = JsonConvert.<CertificadoClave>deserializeObject(CertificadoClave.class, json);
            return cert;
        }
        catch (ProcessingException ex) {
            String mensaje = ex.getMessage();
            if (mensaje != null && !"".equals(mensaje))
                throw new FallaAplicacion(mensaje);
            
            throw ControlException(json);
        }                
        catch (Exception ex) {
            throw new FallaAplicacion(ex.getMessage());
        }
    }
    
    /**
     * 
     * @param json
     * @return 
     */
    private Exception ControlException(String json) {
        ecodex.restapi.models.Error error = null;
        Class<?> type = null;
        try {
            error = JsonConvert.<ecodex.restapi.models.Error>deserializeObject(ecodex.restapi.models.Error.class, json);
            type = Class.forName("ecodex.restapi.exceptions." + error.getError());
        } catch (ClassNotFoundException ex) {
            return new FallaAplicacion(ex.getMessage());
        } catch (Exception ex) {
            return new FallaAplicacion(ex.getMessage());
        }
        if (type == FallaAplicacion.class)
            return new FallaAplicacion(error.getError_description(), error.getError_code());
        else if (type == FallaSesion.class)
            return new FallaSesion(error.getError_description(), error.getError_code());
        else if (type == FallaValidacion.class)
            return new FallaValidacion(error.getError_description(), error.getError_code(), error.getError_suggestion());

        return new FallaAplicacion("Error al materializar la excepción.");
    }    
}
